<!DOCTYPE html>
<html lang="zh-cn" color-mode="light">

  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="keywords" content="" />
  <meta name="author" content="郁涛丶" />
  <meta name="description" content="" />
  
  
  <title>
    
      MIT6.S081_Note 
      
      
      |
    
     郁涛丶&#39;s Blog
  </title>

  
    <link rel="apple-touch-icon" href="/images/favicon.png">
    <link rel="icon" href="/images/favicon.png">
  

  <!-- Raleway-Font -->
  <link href="https://fonts.googleapis.com/css?family=Raleway&display=swap" rel="stylesheet">

  <!-- hexo site css -->
  
<link rel="stylesheet" href="/css/color-scheme.css">
<link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_1886449_67xjft27j1l.css">
<link rel="stylesheet" href="/css/github-markdown.css">
<link rel="stylesheet" href="/css/highlight.css">
<link rel="stylesheet" href="/css/comments.css">

  <!-- 代码块风格 -->
  
    
<link rel="stylesheet" href="/css/figcaption/mac-block.css">

  

  <!-- jquery3.3.1 -->
  
    <script defer type="text/javascript" src="/plugins/jquery.min.js"></script>
  

  <!-- fancybox -->
  
    <link href="/plugins/jquery.fancybox.min.css" rel="stylesheet">
    <script defer type="text/javascript" src="/plugins/jquery.fancybox.min.js"></script>
  
  
<script src="/js/fancybox.js"></script>


  

  <script>
    var html = document.documentElement
    const colorMode = localStorage.getItem('color-mode')
    if (colorMode) {
      document.documentElement.setAttribute('color-mode', colorMode)
    }
  </script>
<!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="郁涛丶's Blog" type="application/atom+xml">
</head>


  <body>
    <div id="app">
      <div class="header">
  <div class="avatar">
    <a href="/">
      <!-- 头像取消懒加载，添加no-lazy -->
      
        <img src="/images/avatar.png" alt="">
      
    </a>
    <div class="nickname"><a href="/">Ghostasky</a></div>
  </div>
  <div class="navbar">
    <ul>
      
        <li class="nav-item" data-path="/">
          <a href="/">Home</a>
        </li>
      
        <li class="nav-item" data-path="/archives/">
          <a href="/archives/">Archives</a>
        </li>
      
        <li class="nav-item" data-path="/categories/">
          <a href="/categories/">Categories</a>
        </li>
      
        <li class="nav-item" data-path="/tags/">
          <a href="/tags/">Tags</a>
        </li>
      
        <li class="nav-item" data-path="/about/">
          <a href="/about/">About</a>
        </li>
      
    </ul>
  </div>
</div>


<script src="/js/activeNav.js"></script>



      <div class="flex-container">
        <!-- 文章详情页，展示文章具体内容，url形式：https://yoursite/文章标题/ -->
<!-- 同时为「标签tag」，「朋友friend」，「分类categories」，「关于about」页面的承载页面，具体展示取决于page.type -->


    <!-- LaTex Display -->

  
    <script async type="text/javascript" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
  
  <script>
    MathJax = {
      tex: {
        inlineMath: [['$', '$'], ['\\(', '\\)']]
      }
    }
  </script>


        
            
                <!-- clipboard -->

  
    <script async type="text/javascript" src="/plugins/clipboard.min.js"></script>
  
  
<script src="/js/codeCopy.js"></script>



                    
                        
                                
                                        
                                                
                                                        
                                                            <!-- 文章内容页 url形式：https://yoursite/文章标题/ -->
                                                            <div class="container post-details" id="post-details">
                                                                <div class="post-content">
                                                                    <div class="post-title">
                                                                        MIT6.S081_Note
                                                                    </div>
                                                                    <div class="post-attach">
                                                                        <span class="post-pubtime">
        <i class="iconfont icon-updatetime" title="Update time"></i>
        2022-07-07
      </span>

                                                                        <span class="post-pubtime"> 本文共10.5k字 </span>

                                                                        <span class="post-pubtime">
        大约需要56min
      </span>

                                                                        
                                                                                    <span class="post-categories">
        <i class="iconfont icon-bookmark" title="Categories"></i>
        
        <span class="span--category">
          <a href="/categories/Technology/" title="Technology">
            <b>#</b> Technology
          </a>
        </span>
                                                                                    
                                                                                        </span>
                                                                                        
                                                                            <span class="post-tags">
        <i class="iconfont icon-tags" title="Tags"></i>
        
        <span class="span--tag">
          <a href="/tags/OS/" title="OS">
            <b>#</b> OS
          </a>
        </span>
                                                                            
                                                                                </span>
                                                                                
                                                                    </div>
                                                                    <div class="markdown-body">
                                                                        <p>[toc]</p>
<blockquote>
<p>  6.s081看的是文档（比较快些），没看视频，：<a target="_blank" rel="noopener" href="https://github.com/Ghostasky/MIT6.S081">https://github.com/Ghostasky/MIT6.S081</a></p>
<p>  XV6:<a target="_blank" rel="noopener" href="https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf">https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf</a></p>
</blockquote>
<p>有一说一，这课是真他娘好看，就按照章节顺序来做笔记吧。</p>
<blockquote>
<p>  上次没看完，，，这个暑假搞完，，，，</p>
</blockquote>
<h1 id="Chapter1"><a href="#Chapter1" class="headerlink" title="Chapter1"></a>Chapter1</h1><h2 id="6-s081"><a href="#6-s081" class="headerlink" title="6.s081"></a>6.s081</h2><p>操作系统的目标：</p>
<ol>
<li>抽象硬件(Abstraction)。对CPU，内存这些，使用应用程序实现高层级的接口和抽象，例如进程，文件系统。</li>
<li>多个应用程序之间共用硬件资源(multiplex)。</li>
<li>隔离性(Isolation)：多个程序之间互不干扰</li>
<li>共享(Sharing)：数据交互，协同完成任务等</li>
<li>Security(或者Permission System或者是Access Control System)</li>
<li>高性能(Performance)</li>
<li>支持大量不同类型的程序</li>
</ol>
<h3 id="操作系统结构"><a href="#操作系统结构" class="headerlink" title="操作系统结构"></a>操作系统结构</h3><p>在这门课程中，我们主要关注点在Kernel、连接Kernel和用户空间程序的接口、Kernel内软件的架构；会关心Kernel中的服务，其中一个服务是文件系统，另一个就是进程管理系统。</p>
<p>每一个用户空间程序都被称为一个进程，它们有自己的内存和共享的CPU时间。</p>
<p>应用程序是与Kernel交互通过系统调用实现。</p>
<p><code>fork()：创建了一个与调用进程一模一样的新的进程，并返回新进程的process ID/pid</code></p>
<p>当程序员在写普通的应用程序时，应用程序下面都是操作系统。而当我们在构建操作系统时，在操作系统下面就是硬件了，这些硬件通常会更难处理。在这门课程中，我们会使用一个叫做QEMU的硬件模拟器，来模拟CPU和计算机。</p>
<h3 id="read-write-exit系统调用"><a href="#read-write-exit系统调用" class="headerlink" title="read,write,exit系统调用"></a>read,write,exit系统调用</h3><p>以下都使用XV6，XV6运行在一个RISC-V微处理器上，会在一个QEMU模拟器上运行XV6。</p>
<p>首先是copy程序：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&quot;kernel/types.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&quot;user/user.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> buf[<span class="number">64</span>];</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> n = read(<span class="number">0</span>, buf, <span class="keyword">sizeof</span>(buf));</span><br><span class="line">        <span class="keyword">if</span> (n &lt;= <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        write(<span class="number">1</span>, buf, n);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意在Makefile里面：</p>
<figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">UPROGS=\</span><br><span class="line">	$U/_cat\</span><br><span class="line">	$U/_echo\</span><br><span class="line">	$U/_forktest\</span><br><span class="line">	$U/_grep\</span><br><span class="line">	$U/_init\</span><br><span class="line">	$U/_kill\</span><br><span class="line">	$U/_ln\</span><br><span class="line">	$U/_ls\</span><br><span class="line">	$U/_mkdir\</span><br><span class="line">	$U/_rm\</span><br><span class="line">	$U/_sh\</span><br><span class="line">	$U/_stressfs\</span><br><span class="line">	$U/_usertests\</span><br><span class="line">	$U/_grind\</span><br><span class="line">	$U/_wc\</span><br><span class="line">	$U/_zombie\</span><br><span class="line">	$U/_copy</span><br></pre></td></tr></table></figure>

<p>read系统调用：</p>
<ol>
<li>第一个参数为文件描述符，文件描述符0连接到console的输入，文件描述符1连接到了console的输出。</li>
<li>第二个参数是指向某段内存的指针</li>
<li>第三个参数是代码想读取的最大长度</li>
</ol>
<p>grep x会搜索输入中包含x的行，我可以告诉shell将输入重定向到文件out，这样我们就可以查看out中的x。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ grep a &lt;out</span><br><span class="line">xargstest.sh   2 3 93</span><br><span class="line">cat            2 4 23968</span><br></pre></td></tr></table></figure>



<h3 id="环境搭建："><a href="#环境搭建：" class="headerlink" title="环境搭建："></a>环境搭建：</h3><p>依据官网：<a target="_blank" rel="noopener" href="https://pdos.csail.mit.edu/6.828/2020/tools.html">https://pdos.csail.mit.edu/6.828/2020/tools.html</a></p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu</span><br><span class="line">sudo apt-get remove qemu-system-misc</span><br><span class="line">sudo apt-get install qemu-system-misc=1:4.2-3ubuntu6</span><br><span class="line">git <span class="built_in">clone</span> git://g.csail.mit.edu/xv6-labs-2020</span><br><span class="line"><span class="built_in">cd</span> xv6-labs-2020</span><br><span class="line">git checkout util</span><br><span class="line">sudo make qemu</span><br></pre></td></tr></table></figure>

<p>test:</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">test</span>:</span><br><span class="line">$ riscv64-unknown-elf-gcc --version</span><br><span class="line">riscv64-unknown-elf-gcc (GCC) 10.1.0</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">$ qemu-system-riscv64 --version</span><br><span class="line">QEMU emulator version 5.1.0</span><br><span class="line"><span class="comment"># in the xv6 directory</span></span><br><span class="line">$ make qemu</span><br><span class="line"><span class="comment"># ... lots of output ...</span></span><br><span class="line">init: starting sh</span><br><span class="line">$</span><br><span class="line"><span class="comment">#success</span></span><br></pre></td></tr></table></figure>

<h3 id="fork"><a href="#fork" class="headerlink" title="fork"></a>fork</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> pid;</span><br><span class="line"> pid = fork();</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">&quot;fork return %d\n&quot;</span>,pid);</span><br><span class="line"> <span class="keyword">if</span>(pid==<span class="number">0</span>)</span><br><span class="line"> &#123;</span><br><span class="line">     <span class="built_in">printf</span>(<span class="string">&quot;child\n&quot;</span>);</span><br><span class="line"> &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">     <span class="built_in">printf</span>(<span class="string">&quot;father\n&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">exit</span>(<span class="number">0</span>);</span><br></pre></td></tr></table></figure>

<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">yutao@ubuntu:~/xv6-labs-2020$ ./a</span><br><span class="line">fork <span class="built_in">return</span> 67703</span><br><span class="line">father</span><br><span class="line">fork <span class="built_in">return</span> 0</span><br><span class="line">child</span><br></pre></td></tr></table></figure>

<p>fork系统调用在两个进程中都会返回，在原始的进程中，fork系统调用会返回大于0的整数，这个是新创建进程的ID。而在新创建的进程中，fork系统调用会返回0。所以即使两个进程的内存是完全一样的，我们还是可以通过fork的返回值区分旧进程和新进程。</p>
<p>父子进程拥有不同的内存空间和寄存器，改变一个进程中的变量不会影响另一个进程。</p>
<h3 id="close"><a href="#close" class="headerlink" title="close"></a>close</h3><p>形式是<code>int close(int fd)</code>，将打开的文件<code>fd</code>释放，使该文件描述符可以被后面的<code>open</code>、<code>pipe</code>等其他system call使用。</p>
<h3 id="dup"><a href="#dup" class="headerlink" title="dup"></a>dup</h3><p><code>dup</code>。形式是<code>int dup(int fd)</code>，复制一个新的<code>fd</code>指向的I&#x2F;O对象，返回这个新fd值，两个I&#x2F;O对象(文件)的offset相同</p>
<h3 id="pipe"><a href="#pipe" class="headerlink" title="pipe"></a>pipe</h3><p>管道，暴露给进程的一对文件描述符，一个文件描述符用来读，另一个文件描述符用来写，将数据从管道的一端写入，将使其能够被从管道的另一端读出,管道的行为是<strong>FIFO</strong>（先进先出）</p>
<p><code>pipe</code>是一个system call，形式为<code>int pipe(int p[])</code>，<code>p[0]</code>为读取的文件描述符，<code>p[1]</code>为写入的文件描述符</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MSGSIZE 16</span></span><br><span class="line"><span class="keyword">char</span>* msg1 = <span class="string">&quot;hello, world #1&quot;</span>;</span><br><span class="line"><span class="keyword">char</span>* msg2 = <span class="string">&quot;hello, world #2&quot;</span>;</span><br><span class="line"><span class="keyword">char</span>* msg3 = <span class="string">&quot;hello, world #3&quot;</span>;</span><br><span class="line">  </span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">char</span> inbuf[MSGSIZE];</span><br><span class="line">    <span class="keyword">int</span> p[<span class="number">2</span>], i;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">if</span> (pipe(p) &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">/* continued */</span></span><br><span class="line">    <span class="comment">/* write pipe */</span></span><br><span class="line">  </span><br><span class="line">    write(p[<span class="number">1</span>], msg1, MSGSIZE);</span><br><span class="line">    write(p[<span class="number">1</span>], msg2, MSGSIZE);</span><br><span class="line">    write(p[<span class="number">1</span>], msg3, MSGSIZE);</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">3</span>; i++) &#123;</span><br><span class="line">        <span class="comment">/* read pipe */</span></span><br><span class="line">        read(p[<span class="number">0</span>], inbuf, MSGSIZE);</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;% s\n&quot;</span>, inbuf);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="exec，wait系统调用"><a href="#exec，wait系统调用" class="headerlink" title="exec，wait系统调用"></a>exec，wait系统调用</h3><p>系统调用 <code>exec</code> 将从某个文件（通常是可执行文件）里读取内存镜像，并将其替换到调用它的进程的内存空间，这样相当于丢弃了调用进程的内存，并开始执行新加载的指令。</p>
<p>通常来说exec系统调用不会返回，因为exec会完全替换当前进程的内存，相当于当前进程不复存在了，所以exec系统调用已经没有地方能返回了。所以shell的话会先fork，之后在子进程里进行exec</p>
<p><img src="/2022/07/07/6.s081/image-20220708142644161.png" alt="image-20220708142644161"></p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ forkexec</span><br><span class="line">parent waiting</span><br><span class="line">THIS IS ECHO</span><br><span class="line">child <span class="built_in">exit</span> status 0</span><br></pre></td></tr></table></figure>

<p>wait会等待之前创建的子进程退出(只能父等子进程)，其中的 status是子进程退出时的状态，正常退出的是0。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">exec(&quot;echoasasdf&quot;, argv);</span><br><span class="line">printf(&quot;exec failed!\n&quot;);</span><br><span class="line">exit(1111);</span><br><span class="line"></span><br><span class="line">$ forkexec</span><br><span class="line">parent waiting</span><br><span class="line">exec failed!</span><br><span class="line">child exit status 1111</span><br></pre></td></tr></table></figure>







<h1 id="Chapter3"><a href="#Chapter3" class="headerlink" title="Chapter3"></a>Chapter3</h1><blockquote>
<p>  <a target="_blank" rel="noopener" href="https://zhayujie.com/mit6828-env.html">https://zhayujie.com/mit6828-env.html</a></p>
</blockquote>
<ol>
<li>隔离性（isolation）</li>
<li>防御性 (Defensive)</li>
<li>协同调度（Cooperative Scheduling）：在发现自己运行了一段时间之后，需要让别的程序也有机会能运行。这种机制有时候称为协同调度。</li>
<li>kernel mode：特殊权限指令主要是一些直接操纵硬件的指令和设置保护的指令，例如设置page table寄存器、关闭时钟中断。在处理器上有各种各样的状态，操作系统会使用这些状态，但是只能通过特殊权限指令来变更这些状态。</li>
</ol>
<p>RISC-V的模式其实是三种：（user&#x2F;kernel&#x2F;machine）</p>
<p>内核有时候也被称为可被信任的计算空间（Trusted Computing Base）</p>
<p>宏内核 vs 微内核 （Monolithic Kernel vs Micro Kernel）</p>
<p>宏内核：整个操作系统代码都运行在kernel mode。大多数的Unix操作系统实现都运行在kernel mode。比如，XV6中，所有的操作系统服务都在kernel mode中，这种形式被称为Monolithic Kernel Design。宏内核的优势在于，因为这些子模块现在都位于同一个程序中，它们可以紧密的集成在一起，这样的集成提供很好的性能。例如Linux，它就有很不错的性能。</p>
<p>微内核：希望在kernel mode中运行尽可能少的代码。所以这种设计下还是有内核，但是内核只有非常少的几个模块。</p>
<p>需要有一种方式能够让应用程序可以将控制权转移给内核（Entering Kernel）。</p>
<p>在RISC-V中，有一个专门的指令用来实现这个功能，叫做ECALL。ECALL接收一个数字参数，当一个用户程序想要将程序执行的控制权转移到内核，它只需要执行ECALL指令，并传入一个数字。这里的数字参数代表了应用程序想要调用的System Call。</p>
<p>可被信任的计算空间（Trusted Computing Base）TCB</p>
<p>IPC（Inter-Process Communication，进程间通信）</p>
<p>对于任何文件系统的交互，都需要分别完成2次用户空间&lt;-&gt;内核空间的跳转。与宏内核对比，在宏内核中如果一个应用程序需要与文件系统交互，只需要完成1次用户空间&lt;-&gt;内核空间的跳转，所以微内核的的跳转是宏内核的两倍。</p>
<h2 id="编译运行kernel"><a href="#编译运行kernel" class="headerlink" title="编译运行kernel"></a>编译运行kernel</h2><p>xv6为宏内核</p>
<p>kernel：包含所有内核文件，里所有的文件会被编译成一个叫做kernel的二进制文件，然后这个二进制文件会被运行在kernle mode中。</p>
<p>mkfs：它会创建一个空的文件镜像，我们会将这个镜像存在磁盘上，这样我们就可以直接使用一个空的文件系统。</p>
<p>编译内核过程：</p>
<ul>
<li>首先，Makefile（XV6目录下的文件）会读取一个C文件，例如proc.c；之后调用gcc编译器，生成一个文件叫做proc.s，这是RISC-V 汇编语言文件；之后再走到汇编解释器，生成proc.o，这是汇编语言的二进制格式。</li>
<li>Makefile会为所有内核文件做相同的操作。</li>
<li>之后，系统加载器（Loader）会收集所有的.o文件，将它们链接在一起，并生成内核文件。</li>
</ul>
<p>这里为了方便还会生成kernel.asm，包含了内核的完整汇编语言:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">kernel/kernel:     file format elf64-littleriscv</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Disassembly of section .text:</span><br><span class="line"></span><br><span class="line">0000000080000000 &lt;_entry&gt;:</span><br><span class="line">    80000000:	0000a117          	auipc	sp,0xa</span><br><span class="line">    80000004:	83010113          	addi	sp,sp,-2000 # 80009830 &lt;stack0&gt;</span><br><span class="line">    80000008:	6505                	lui	a0,0x1</span><br><span class="line">    8000000a:	f14025f3          	csrr	a1,mhartid</span><br><span class="line">    8000000e:	0585                	addi	a1,a1,1</span><br><span class="line">    80000010:	02b50533          	mul	a0,a0,a1</span><br><span class="line">    80000014:	912a                	add	sp,sp,a0</span><br><span class="line">    80000016:	070000ef          	jal	ra,80000086 &lt;start&gt;</span><br><span class="line"></span><br><span class="line">000000008000001a &lt;spin&gt;:</span><br><span class="line">    8000001a:	a001                	j	8000001a &lt;spin&gt;</span><br><span class="line"></span><br><span class="line">000000008000001c &lt;timerinit&gt;:</span><br><span class="line">// which arrive at timervec in kernelvec.S,</span><br><span class="line">// which turns them into software interrupts for</span><br><span class="line">// devintr() in trap.c.</span><br><span class="line">void</span><br><span class="line">timerinit()</span><br><span class="line">&#123;</span><br><span class="line">    8000001c:	1141                	addi	sp,sp,-16</span><br><span class="line">    8000001e:	e422                	sd	s0,8(sp)</span><br><span class="line">    80000020:	0800                	addi	s0,sp,16</span><br><span class="line">// which hart (core) is this?</span><br><span class="line">static inline uint64</span><br><span class="line">r_mhartid()</span><br><span class="line">&#123;</span><br><span class="line">.........</span><br></pre></td></tr></table></figure>

<p>可以看到第一个指令在0x80000000.</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">yutao@ubuntu:~/xv6-labs-2020$ sudo make qemu</span><br><span class="line">qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,<span class="keyword">if</span>=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0</span><br><span class="line"></span><br><span class="line">xv6 kernel is booting</span><br><span class="line"></span><br><span class="line">hart 2 starting</span><br><span class="line">hart 1 starting</span><br><span class="line">init: starting sh</span><br><span class="line">$ </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>传给QEMU的几个参数：</p>
<ul>
<li>-kernel：这里传递的是内核文件（kernel目录下的kernel文件），这是将在QEMU中运行的程序文件。</li>
<li>-m：这里传递的是RISC-V虚拟机将会使用的内存数量</li>
<li>-smp：这里传递的是虚拟机可以使用的CPU核数</li>
<li>-drive：传递的是虚拟机使用的磁盘驱动，这里传入的是fs.img文件</li>
</ul>
<p>当我们说QEMU仿真了RISC-V处理器时，背后的含义：</p>
<p>直观来看，QEMU是一个大型的开源C程序，你可以下载或者git clone它。但是在内部，在QEMU的主循环中，只在做一件事情：</p>
<ul>
<li>读取4字节或者8字节的RISC-V指令。</li>
<li>解析RISC-V指令，并找出对应的操作码（op code）。我们之前在看kernel.asm的时候，看过一些操作码的二进制版本。通过解析，或许可以知道这是一个ADD指令，或者是一个SUB指令。</li>
<li>之后，在软件中执行相应的指令。</li>
</ul>
<h2 id="xv6启动过程"><a href="#xv6启动过程" class="headerlink" title="xv6启动过程"></a>xv6启动过程</h2><p>启动qemu，打开gdb：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">yutao@ubuntu:~/xv6-labs-2020$ sudo make CPUS=1 qemu-gdb </span><br><span class="line">*** Now run <span class="string">&#x27;gdb&#x27;</span> <span class="keyword">in</span> another window.</span><br><span class="line">qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 1 -nographic -drive file=fs.img,<span class="keyword">if</span>=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::25000</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>在xv6的目录再打开一个终端：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gdb-multiarch kernel/kernel</span><br><span class="line"><span class="comment">#至于还要装什么我也忘了，鼓弄一下午，，，，fk</span></span><br></pre></td></tr></table></figure>

<p>常用gdb指令：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">layout split        # 同时打开源码及汇编窗口</span><br><span class="line">layout reg          # 打开寄存器窗口</span><br><span class="line">layout asm          # 打开汇编窗口</span><br><span class="line">next / nexti        # 单步到下一行 源代码 / 指令，不进入函数</span><br><span class="line">step / stepi        # 单步到下一行 源代码 / 指令，进入函数</span><br><span class="line">break (b)           # 设置断点，后面可接函数、行号、地址等</span><br><span class="line">continue (c)        # 继续执行到下一个断点</span><br></pre></td></tr></table></figure>

<p>进去之后断在_entry</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">For <span class="built_in">help</span>, <span class="built_in">type</span> <span class="string">&quot;help&quot;</span>.</span><br><span class="line">Type <span class="string">&quot;apropos word&quot;</span> to search <span class="keyword">for</span> commands related to <span class="string">&quot;word&quot;</span>...</span><br><span class="line">Reading symbols from kernel/kernel...</span><br><span class="line">The target architecture is assumed to be riscv:rv64</span><br><span class="line">0x0000000000001000 <span class="keyword">in</span> ?? ()</span><br><span class="line">(gdb) b _entry</span><br><span class="line">Breakpoint 1 at 0x8000000a</span><br><span class="line">(gdb) c</span><br><span class="line">Continuing.</span><br><span class="line"></span><br><span class="line">Breakpoint 1, 0x000000008000000a <span class="keyword">in</span> _entry ()</span><br><span class="line">=&gt; 0x000000008000000a &lt;_entry+10&gt;:	f3 25 40 f1	csrr	a1,mhartid</span><br><span class="line">(gdb) </span><br></pre></td></tr></table></figure>

<p>这里可以看到，XV6从entry.s开始启动，这个时候没有内存分页，没有隔离性，并且运行在M-mode（machine mode）。XV6会尽可能快的跳转到kernel mode或者说是supervisor mode。我们在main函数设置一个断点，main函数已经运行在supervisor mode了。接下来我运行程序，代码会在断点，也就是main函数的第一条指令停住。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(gdb) b main</span><br><span class="line">Breakpoint 2 at 0x80000ec6: file kernel/main.c, line 13.</span><br></pre></td></tr></table></figure>

<p>进入 layout split 模式：</p>
<p><img src="/2022/07/07/6.s081/image-20211127204219036.png" alt="image-20211127204219036"></p>
<p>main.c</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span></span></span><br><span class="line"><span class="function"><span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(cpuid() == <span class="number">0</span>)&#123;</span><br><span class="line">    consoleinit();</span><br><span class="line">    printfinit();</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;xv6 kernel is booting\n&quot;</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">    kinit();         <span class="comment">// physical page allocator</span></span><br><span class="line">    kvminit();       <span class="comment">// create kernel page table</span></span><br><span class="line">    kvminithart();   <span class="comment">// turn on paging</span></span><br><span class="line">    procinit();      <span class="comment">// process table</span></span><br><span class="line">    trapinit();      <span class="comment">// trap vectors</span></span><br><span class="line">    trapinithart();  <span class="comment">// install kernel trap vector</span></span><br><span class="line">    plicinit();      <span class="comment">// set up interrupt controller</span></span><br><span class="line">    plicinithart();  <span class="comment">// ask PLIC for device interrupts</span></span><br><span class="line">    binit();         <span class="comment">// buffer cache</span></span><br><span class="line">    iinit();         <span class="comment">// inode cache</span></span><br><span class="line">    fileinit();      <span class="comment">// file table</span></span><br><span class="line">    virtio_disk_init(); <span class="comment">// emulated hard disk</span></span><br><span class="line">    userinit();      <span class="comment">// first user process</span></span><br><span class="line">    __sync_synchronize();</span><br><span class="line">    started = <span class="number">1</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">while</span>(started == <span class="number">0</span>)</span><br><span class="line">      ;</span><br><span class="line">    __sync_synchronize();</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;hart %d starting\n&quot;</span>, cpuid());</span><br><span class="line">    kvminithart();    <span class="comment">// turn on paging</span></span><br><span class="line">    trapinithart();   <span class="comment">// install kernel trap vector</span></span><br><span class="line">    plicinithart();   <span class="comment">// ask PLIC for device interrupts</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  scheduler();        </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>有很多初始化的函数，顺序也很重要：</p>
<ul>
<li>kinit：设置好页表分配器（page allocator）</li>
<li>kvminit：设置好虚拟内存，这是下节课的内容</li>
<li>kvminithart：打开页表，也是下节课的内容</li>
<li>processinit：设置好初始进程或者说设置好进程表单</li>
<li>trapinit&#x2F;trapinithart：设置好user&#x2F;kernel mode转换代码</li>
<li>plicinit&#x2F;plicinithart：设置好中断控制器PLIC（Platform Level Interrupt Controller），我们后面在介绍中断的时候会详细的介绍这部分，这是我们用来与磁盘和console交互方式</li>
<li>binit：分配buffer cache</li>
<li>iinit：初始化inode缓存</li>
<li>fileinit：初始化文件系统</li>
<li>virtio_disk_init：初始化磁盘</li>
<li>userinit：最后当所有的设置都完成了，操作系统也运行起来了，会通过userinit运行第一个进程，这里有点意思，接下来我们看一下userinit</li>
</ul>
<p>跟userinit：</p>
<p><img src="/2022/07/07/6.s081/image-20211127204927777.png" alt="image-20211127204927777"></p>
<p>实际上initcode就是执行了exec(“&#x2F;init”)</p>
<p>断在syscall：</p>
<p><img src="/2022/07/07/6.s081/image-20211127205221097.png" alt="image-20211127205221097"></p>
<p><code>num = p-&gt;trapframe-&gt;a7;</code>读取使用的系统调用的整数，执行完：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(gdb) p num</span><br><span class="line"><span class="variable">$1</span> = 7</span><br></pre></td></tr></table></figure>

<p><img src="/2022/07/07/6.s081/image-20211127205509797.png" alt="image-20211127205509797"></p>
<p>是exc系统调用。</p>
<p>之后的<code> p-&gt;trapframe-&gt;a0 = syscalls[num]();</code>执行系统调用，跟到syscalls中去：</p>
<p><img src="/2022/07/07/6.s081/image-20211127205923786.png" alt="image-20211127205923786"></p>
<p>sys_exec会从用户空间读取参数，它会读取path，也就是要执行程序的文件名。这里首先会为参数分配空间，然后从用户空间将参数拷贝到内核空间。</p>
<p>传入的是init程序，看下</p>
<p><img src="/2022/07/07/6.s081/image-20211127210234975.png" alt="image-20211127210234975"></p>
<p>init会为用户空间设置好一些东西，比如配置好console，调用fork，并在fork出的子进程中执行shell。</p>
<p>然后就可以在qemu中看到shell起来了</p>
<h1 id="Chapter4"><a href="#Chapter4" class="headerlink" title="Chapter4"></a>Chapter4</h1><p>页表，内存管理单元（Memory Management Unit）</p>
<p><img src="/2022/07/07/6.s081/image-20211128000721204.png" alt="image-20211128000721204"></p>
<p>page table保存在内存中，MMU只是会去查看page table，我们接下来会看到，page table比我们这里画的要稍微复杂一些。</p>
<p>当操作系统将CPU从一个应用程序切换到另一个应用程序时，同时也需要切换SATP寄存器中的内容，从而指向新的进程保存在物理内存中的地址对应表单。</p>
<p>RISC-V中，一个page是4KB。</p>
<p>首先对于虚拟内存地址，我们将它划分为两个部分，index和offset，index用来查找page，offset对应的是一个page中的哪个字节。</p>
<p>当MMU在做地址翻译的时候，通过读取虚拟内存地址中的index可以知道物理内存中的page号，将offset加上page的起始地址，就可以得到物理内存地址。</p>
<p>实际上，在我们使用的RSIC-V处理器上，并不是所有的64bit都被使用了，也就是说高25bit并没有被使用。这样的结果是限制了虚拟内存地址的数量，虚拟内存地址的数量现在只有2^39个，大概是512GB。</p>
<p>在剩下的39bit中，有27bit被用来当做index，12bit被用来当做offset。offset必须是12bit，因为对应了一个page的4096个字节。</p>
<p>在RISC-V中，物理内存地址是56bit。其中44bit是物理page号（PPN，Physical Page Number），剩下12bit是offset完全继承自虚拟内存地址（也就是地址转换时，只需要将虚拟内存中的27bit翻译成物理内存中的44bit的page号，剩下的12bitoffset直接拷贝过来即可）。</p>
<p>实际上page table是一个多级的结构，下图是一个真正的RISC-V page table结构和硬件实现。3级结构是由硬件实现而不是系统。</p>
<p><img src="/2022/07/07/6.s081/image-20211128001754166.png" alt="image-20211128001754166"></p>
<p>27bit的index，实际上是由3个9bit的数字组成（L2，L1，L0）。前9个bit被用来索引最高级的page directory。Directory中的一个条目被称为PTE（Page Table Entry）是64bits，就像寄存器的大小一样，也就是8Bytes。所以一个Directory page有512个条目。</p>
<p>实际上，SATP寄存器会指向最高一级的page directory的物理内存地址，之后我们用虚拟内存中index的高9bit用来索引最高一级的page directory，这样我们就能得到一个PPN，也就是物理page号。这个PPN指向了中间级的page directory。</p>
<p>当我们在使用中间级的page directory时，我们通过虚拟内存地址中的L1部分完成索引。接下来会走到最低级的page directory，我们通过虚拟内存地址中的L0部分完成索引。在最低级的page directory中，我们可以得到对应于虚拟内存地址的物理内存地址。</p>
<p>之前的方案要用到2^27个PTE，现在这个方案中，只需要3 * 512个PTE，大大减少</p>
<p>接下来看看PTE中的Flag。每个PTE的低10bit是一堆标志位：</p>
<ul>
<li>第一个标志位是Valid。如果Valid bit位为1，那么表明这是一条合法的PTE，你可以用它来做地址翻译。对于刚刚举得那个小例子（应用程序只用了1个page的例子），我们只使用了3个page directory，每个page directory中只有第0个PTE被使用了，所以只有第0个PTE的Valid bit位会被设置成1，其他的511个PTE的Valid bit为0。这个标志位告诉MMU，你不能使用这条PTE，因为这条PTE并不包含有用的信息。</li>
<li>下两个标志位分别是Readable和Writable。表明你是否可以读&#x2F;写这个page。</li>
<li>Executable表明你可以从这个page执行指令。</li>
<li>User表明这个page可以被运行在用户空间的进程访问。</li>
<li>其他标志位并不是那么重要。</li>
</ul>
<p>这里还会用到页表缓存（Translation Lookaside Buffer）：对于一个虚拟内存地址的寻址，需要读三次内存，这里代价有点高。所以实际中，几乎所有的处理器都会对于最近使用过的虚拟地址的翻译结果有缓存。</p>
<p>接下来看XV6中，page table是如何工作的。</p>
<p>下图就是内核中地址的对应关系，左边是内核的虚拟地址空间，右边上半部分是物理内存或者说是DRAM，右边下半部分是I&#x2F;O设备。</p>
<p><img src="/2022/07/07/6.s081/image-20211128230803818.png" alt="image-20211128230803818"></p>
<p>或者说是这样：</p>
<p><img src="/2022/07/07/6.s081/image-20211128230837614.png" alt="image-20211128230837614"></p>
<p>上面那个图的右侧，地址0x1000是boot ROM的物理地址，当你对主板上电，主板做的第一件事情就是运行存储在boot ROM中的代码，当boot完成之后，会跳转到地址0x80000000。</p>
<p>其他的一些IO设备：</p>
<ul>
<li>PLIC是中断控制器（Platform-Level Interrupt Controller）。</li>
<li>CLINT（Core Local Interruptor）也是中断的一部分。所以多个设备都能产生中断，需要中断控制器来将这些中断路由到合适的处理函数。</li>
<li>UART0（Universal Asynchronous Receiver&#x2F;Transmitter）负责与Console和显示器交互。</li>
<li>VIRTIO disk，与磁盘进行交互。</li>
</ul>
<p>低于0x80000000的物理地址，不存在于DRAM中，当我们在使用这些地址的时候，指令会直接走向其他的硬件。</p>
<p>有一些page在虚拟内存中的地址很靠后，比如kernel stack在虚拟内存中的地址就很靠后。这是因为在它之下有一个未被映射的Guard page，这个Guard page对应的PTE的Valid 标志位没有设置，这样，如果kernel stack耗尽了，它会溢出到Guard page，但是因为Guard page的PTE中Valid标志位未设置，会导致立即触发page fault，这样的结果好过内存越界之后造成的数据混乱。立即触发一个panic（也就是page fault），你就知道kernel stack出错了。同时我们也又不想浪费物理内存给Guard page，所以Guard page不会映射到任何物理内存，它只是占据了虚拟地址空间的一段靠后的地址。</p>
<p>同时，kernel stack被映射了两次，在靠后的虚拟地址映射了一次，在PHYSTOP下的Kernel data中又映射了一次，但是实际使用的时候用的是上面的部分，因为有Guard page会更加安全。</p>
<p>该跟着调4.6了</p>
<p>这里应该先跳过了，先干后面的。</p>
<h2 id="kvminit函数"><a href="#kvminit函数" class="headerlink" title="kvminit函数"></a>kvminit函数</h2><p><img src="/2022/07/07/6.s081/image-20220709140553688.png" alt="image-20220709140553688"></p>
<p>首先分配物理page，就是kalloc那里，之后初始化内存为0，之后将每个I&#x2F;O设备映射到内核，</p>
<p>在memlayout.h中，可以看到映射地址：<img src="/2022/07/07/6.s081/image-20220709140815091.png" alt="image-20220709140815091"></p>
<p>通过kvmmap可以将物理地址映射到相同的虚拟地址（注，因为kvmmap的前两个参数一致）</p>
<h1 id="Chapter5"><a href="#Chapter5" class="headerlink" title="Chapter5"></a>Chapter5</h1><blockquote>
<p>  导读：<a target="_blank" rel="noopener" href="https://pdos.csail.mit.edu/6.828/2020/readings/riscv-calling.pdf">https://pdos.csail.mit.edu/6.828/2020/readings/riscv-calling.pdf</a></p>
</blockquote>
<p>这里用到的asm不是x86而是RISC-V，精简指令集（诞生于uc berkeley），而x86是复杂是复杂指令集CISC。</p>
<p>RISC指令集开源相关文档在课程页可找到，包含特殊权限指令和普通指令。相比x86的文档小了很多。</p>
<blockquote>
<p>  5.3的一些图片被删了，commit里面可以找到：<a target="_blank" rel="noopener" href="https://github.com/huihongxiao/MIT6.S081/commit/6e5a0d8c2a3840bc9d3a8a381ff491567f1f9ee9%E3%80%82">https://github.com/huihongxiao/MIT6.S081/commit/6e5a0d8c2a3840bc9d3a8a381ff491567f1f9ee9。</a></p>
</blockquote>
<p>用到的RISC指令：</p>
<p><img src="/2022/07/07/6.s081/image-20211129172038380.png"></p>
<p>然后是stack相关的内容，和之前pwn的内容重合，就不写了，但是里面一些gdb的指令还是可以看看的。</p>
<p>在gdb中输入layout asm，可以在tui窗口看到所有的汇编指令。再输入layout reg可以看到所有的寄存器信息。</p>
<h1 id="Chapter6"><a href="#Chapter6" class="headerlink" title="Chapter6"></a>Chapter6</h1><blockquote>
<p>  导读：阅读【1】中第4章，除了4.6；阅读RISCV.h【2】；阅读trampoline.S【3】；阅读trap.c【4】</p>
<p>  【1】<a target="_blank" rel="noopener" href="https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf">https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf</a></p>
<p>  【2】<a target="_blank" rel="noopener" href="https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h">https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/riscv.h</a></p>
<p>  【3】<a target="_blank" rel="noopener" href="https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/trampoline.S">https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/trampoline.S</a></p>
<p>  【4】<a target="_blank" rel="noopener" href="https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/trap.c">https://github.com/mit-pdos/xv6-riscv/blob/riscv/kernel/trap.c</a></p>
</blockquote>
<p>RISC-V总共有32个比如a0，a1这样的寄存器，用户应用程序可以使用全部的寄存器。</p>
<p>一些重要的寄存器介绍：</p>
<ul>
<li><code>stvec</code>：内核在这里写入其陷阱处理程序的地址；RISC-V跳转到这里处理陷阱。</li>
<li><code>sepc</code>：当发生陷阱时，RISC-V会在这里保存程序计数器<code>pc</code>（因为<code>pc</code>会被<code>stvec</code>覆盖）。<code>sret</code>（从陷阱返回）指令会将<code>sepc</code>复制到<code>pc</code>。内核可以写入<code>sepc</code>来控制<code>sret</code>的去向。</li>
<li><code>scause</code>： RISC-V在这里放置一个描述陷阱原因的数字。</li>
<li><code>sscratch</code>：内核在这里放置了一个值，这个值在陷阱处理程序一开始就会派上用场。</li>
<li><code>sstatus</code>：其中的<strong>SIE</strong>位控制设备中断是否启用。如果内核清空<strong>SIE</strong>，RISC-V将推迟设备中断，直到内核重新设置<strong>SIE</strong>。<strong>SPP</strong>位指示陷阱是来自用户模式还是管理模式，并控制<code>sret</code>返回的模式。</li>
</ul>
<h2 id="trap的执行流程"><a href="#trap的执行流程" class="headerlink" title="trap的执行流程"></a>trap的执行流程</h2><p>这里使用write来举例，ecall执行系统调用，ecall后会切换到内核，内核中执行的第一个指令是一个由汇编语言写的函数，叫做<code>uservec</code>（<code>trampoline.s</code>），执行完后会跳到<code>usertrap</code>(<code>trap.c</code>)，<code>usertrap</code>中会执行<code>syscall</code>，<code>syscall</code>会在一个表单中，根据传入的代表系统调用的数字进行查找，并在内核中执行具体实现了系统调用功能的函数。</p>
<p>之后会输出内容到终端，完成后返回到<code>syscall</code>，再之后回复user空间代码，<code>syscall</code>中调用了<code>usertrapret</code>(<code>trap.c</code>)，<code>usertrapret</code>完成了部分方便在C代码中实现的返回到用户空间的工作，剩下的一些工作由汇编完成，即<code>userret</code>（<code>trampoline.s</code>），完成后会返回到用户空间继续执行用户代码。</p>
<blockquote>
<p>  vm.c运行在kernel mode下。</p>
</blockquote>
<h2 id="ECALL指令之前状态"><a href="#ECALL指令之前状态" class="headerlink" title="ECALL指令之前状态"></a>ECALL指令之前状态</h2><p><code>sh.c</code>:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">getcmd</span><span class="params">(<span class="keyword">char</span> *buf, <span class="keyword">int</span> nbuf)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">// fprintf(2, &quot;$ &quot;);</span></span><br><span class="line">  write(<span class="number">2</span>, <span class="string">&quot;$&quot;</span>, <span class="number">2</span>);</span><br><span class="line">  <span class="built_in">memset</span>(buf, <span class="number">0</span>, nbuf);</span><br><span class="line">  gets(buf, nbuf);</span><br><span class="line">  <span class="keyword">if</span> (buf[<span class="number">0</span>] == <span class="number">0</span>) <span class="comment">// EOF</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>write系统调用，它将“$ ”写入到文件描述符2.</p>
<p>用户代码的Shell调用write时，实际上调用的是关联到Shell的一个库函数。在usys.s。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">write:</span><br><span class="line"> li a7, SYS_write</span><br><span class="line"> ecall</span><br><span class="line"> ret</span><br></pre></td></tr></table></figure>

<p>看下<code>sh.asm</code>,，找到ecall的指令的地址，下断点：是0xdee</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">0000000000000dea &lt;write&gt;:</span><br><span class="line">.global write</span><br><span class="line">write:</span><br><span class="line"> li a7, SYS_write</span><br><span class="line">     dea:	48c1                	li	a7,16</span><br><span class="line"> ecall</span><br><span class="line">     dec:	00000073          	ecall</span><br><span class="line"> ret</span><br><span class="line">     df0:	8082                	ret</span><br></pre></td></tr></table></figure>

<p>下断点：可以看下PC寄存器</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">(gdb) b *0xdec</span><br><span class="line">Breakpoint 1 at 0xdee</span><br><span class="line">(gdb) c</span><br><span class="line">Continuing.</span><br><span class="line">[Switching to Thread 1.2]</span><br><span class="line"></span><br><span class="line">Thread 2 hit Breakpoint 1, 0x0000000000000dee <span class="keyword">in</span> ?? ()</span><br><span class="line">=&gt; 0x0000000000000dec:	73 00 00 00	ecall</span><br><span class="line">(gdb) <span class="built_in">print</span> <span class="variable">$pc</span></span><br><span class="line"><span class="variable">$1</span> = (void (*)()) 0xdec</span><br></pre></td></tr></table></figure>

<p>还可以<code>info reg</code>打印全部寄存器（user）</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">(gdb) info reg</span><br><span class="line">ra             0xe8c    0xe8c</span><br><span class="line">sp             0x3e90   0x3e90</span><br><span class="line">gp             0x505050505050505        0x505050505050505</span><br><span class="line">tp             0x505050505050505        0x505050505050505</span><br><span class="line">t0             0x505050505050505        361700864190383365</span><br><span class="line">t1             0x505050505050505        361700864190383365</span><br><span class="line">t2             0x505050505050505        361700864190383365</span><br><span class="line">fp             0x3eb0   0x3eb0</span><br><span class="line">s1             0x12f1   4849</span><br><span class="line">a0             0x1      1</span><br><span class="line">a1             0x3e9f   16031</span><br><span class="line">a2             0x1      1</span><br><span class="line">a3             0x505050505050505        361700864190383365</span><br><span class="line">a4             0x505050505050505        361700864190383365</span><br><span class="line">a5             0x24     36</span><br><span class="line">a6             0x505050505050505        361700864190383365</span><br><span class="line">a7             0x10     16</span><br><span class="line">s2             0x24     36</span><br><span class="line">s3             0x0      0</span><br><span class="line">s4             0x25     37</span><br><span class="line">s5             0x2      2</span><br><span class="line">s6             0x3f50   16208</span><br><span class="line">s7             0x1438   5176</span><br><span class="line">s8             0x64     100</span><br></pre></td></tr></table></figure>

<p>a0，a1，a2是Shell传递给write系统调用的参数。所以a0是文件描述符2；a1是Shell想要写入字符串的指针；a2是想要写入的字符数。可以打印shell想要写入的字符串：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(gdb) x/2c <span class="variable">$a1</span></span><br><span class="line">0x12f0:	36 <span class="string">&#x27;$&#x27;</span>	0 <span class="string">&#x27;\000&#x27;</span></span><br></pre></td></tr></table></figure>

<p>系统调用是会有大量状态的变更，其中一个最重要的需要变更的状态，并且在它变更之前我们对它还有依赖的，就是是当前的<code>page table</code>。我们可以查看SATP寄存器：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(gdb) <span class="built_in">print</span>/x <span class="variable">$satp</span></span><br><span class="line"><span class="variable">$3</span> = 0x8000000000087f63</span><br></pre></td></tr></table></figure>

<p>这里输出的是物理地址，page table的映射关系，QEMU可以打印：在qemu界面，ctrl a+c 进到qemu的console，输入<code>info mem</code>：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(qemu) info mem</span><br><span class="line">vaddr            paddr            size             attr</span><br><span class="line">---------------- ---------------- ---------------- -------</span><br><span class="line">0000000000000000 0000000087f60000 0000000000001000 rwxu-a-</span><br><span class="line">0000000000001000 0000000087f5d000 0000000000001000 rwxu-a-</span><br><span class="line">0000000000002000 0000000087f5c000 0000000000001000 rwx----</span><br><span class="line">0000000000003000 0000000087f5b000 0000000000001000 rwxu-ad</span><br><span class="line">0000003fffffe000 0000000087f6f000 0000000000001000 rw---ad</span><br><span class="line">0000003ffffff000 0000000080007000 0000000000001000 r-x--a-</span><br></pre></td></tr></table></figure>

<p>attr这一列是PTE的标志位，u标志位（rwx后），它表明PTE_u标志位是否被设置，用户代码只能访问u标志位设置了的PTE。再下一个标志位是a（Accessed），表明这条PTE是不是被使用过。再下一个标志位d（Dirty）表明这条PTE是不是被写过。</p>
<p>最后两条PTE的虚拟地址非常大，非常接近虚拟地址的顶端，这两个page分别是<code>trapframe page</code>和<code>trampoline page</code>，都没有设置u标志，所以用户代码不能访问这两条PTE。一旦我们进入到了supervisor mode，就可以访问这两条PTE了。</p>
<h2 id="ECALL指令之后状态"><a href="#ECALL指令之后状态" class="headerlink" title="ECALL指令之后状态"></a>ECALL指令之后状态</h2><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">(gdb) x/3i 0xdee</span><br><span class="line">=&gt; 0xdee:	ecall</span><br><span class="line">   0xdf2:	ret</span><br><span class="line">   0xdf4:	li	a7,21</span><br><span class="line">(gdb) stepi</span><br><span class="line">0x0000000000000df2 <span class="keyword">in</span> ?? ()</span><br><span class="line">=&gt; 0x0000000000000df2:	82 80	ret</span><br></pre></td></tr></table></figure>

<p>能看出这里其实没进内核，，，试了好几次也不知道为啥，，</p>
<p>正常情况下，进去之后直接就到高地址了，，，，PC寄存器可以看到。（下面就按照假设进去之后</p>
<p>PC在<code>trampoline page</code>的最开始，要进行的指令是内核在<code>supervisor mode</code>中将要执行的最开始的几条指令，如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># swap a0 and sscratch</span><br><span class="line"># so that a0 is TRAPFRAME</span><br><span class="line">csrrw a0, sscratch, a0</span><br><span class="line"># save the user registers in TRAPFRAME</span><br><span class="line">sd ra, 40(a0)</span><br><span class="line">sd sp, 48(a0)</span><br><span class="line">sd gp, 56(a0)</span><br><span class="line">sd tp, 64(a0)</span><br><span class="line">sd t0, 72(a0)</span><br><span class="line">........</span><br></pre></td></tr></table></figure>

<p><code>csrrw</code>指令交换了寄存器<code>a0</code>和<code>sscratch</code>的内容</p>
<p>现在在这个地址<code>0x3ffffff000</code>，也就是上面page table输出的最后一个page，<code>trampoline page</code>，这个page包含了内核的trap处理代码。ecall并不会切换page table。</p>
<h1 id="Chapter8"><a href="#Chapter8" class="headerlink" title="Chapter8"></a>Chapter8</h1><h2 id="page-fault-basics"><a href="#page-fault-basics" class="headerlink" title="page fault basics"></a>page fault basics</h2><p>这章内容是<code>page fault</code>，以及通过<code>page fault</code>可以实现的一系列虚拟内存功能。XV6中没有实现<code>page fault</code></p>
<p>下一个实验<code>lazy lab</code></p>
<p>虚拟内存的两个主要优点：</p>
<ul>
<li>隔离性</li>
<li>另一个好处是<code>level of indirection</code>，提供了一层抽象。处理器和所有的指令都可以使用虚拟地址，而内核会定义从虚拟地址到物理地址的映射关系。</li>
</ul>
<p>以上介绍的内存地址映射都相对静止，就是分配好就不动了。</p>
<p>下面介绍的<code>page fault</code>使这种映射变得动态起来。</p>
<p>通过page fault，内核可以更新page table。</p>
<p>什么样的信息是<code>page fault</code>必须得，或者说内核需要什么样的信息才能响应<code>page fault</code>？</p>
<ul>
<li>出错的虚拟地址</li>
<li>出错的原因</li>
<li>触发page fault的指令的地址：存放在SEPC（Supervisor Exception Program Counter）寄存器中，并同时会保存在trapframe-&gt;epc</li>
</ul>
<p>所以说有价值的信息为：引起page fault的内存地址、原因类型、程序计数器值。</p>
<h2 id="lazy-page-allocation"><a href="#lazy-page-allocation" class="headerlink" title="lazy page allocation"></a>lazy page allocation</h2><p>这小结讲的是Allocate，或者说是<code>sbrk</code>，sbrk是xv6的系统调用，用于增大heap，这里xv6的heap在stack的上面，即高地址，最起码ppt是这样显示的。</p>
<p>那么最开始的时候sbrk指向heap的底端，也就是stack的顶端，用sz字段表示，这里以<code>p-&gt;sz</code>表示。</p>
<p>这意味着，当sbrk实际发生或者被调用的时候，内核会分配一些物理内存，并将这些内存映射到用户应用程序的地址空间，然后将内存内容初始化为0，再返回sbrk系统调用。当然参数可正可负，本节只关注增加内存的情况。</p>
<p>sbrk的系统调用做的事情就是提升<code>p-&gt;sz</code>，加n，n是需要的page数。之后要将新的内存映射到page table。</p>
<p>改下sbrk的调用的代码：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">uint64</span></span><br><span class="line"><span class="function"><span class="title">sys_sbrk</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> addr;</span><br><span class="line">	<span class="keyword">int</span> n;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (argint(<span class="number">0</span>, &amp;n) &lt; <span class="number">0</span>)</span><br><span class="line">		<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">	addr = myproc()-&gt;sz;</span><br><span class="line">	myproc()-&gt;sz = myproc()-&gt;sz + n;</span><br><span class="line">	<span class="comment">// if (growproc(n) &lt; 0)</span></span><br><span class="line">	<span class="comment">//   return -1;</span></span><br><span class="line">	<span class="keyword">return</span> addr;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>之后echo下：</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> hi</span><br><span class="line">usertrap(): unexpected scause 0x000000000000000f pid=3</span><br><span class="line">            sepc=0x00000000000012ac stval=0x0000000000004008</span><br><span class="line">panic: uvmunmap: not mapped</span><br></pre></td></tr></table></figure>

<p>输出了：</p>
<ul>
<li>SCAUSE寄存器内容：表明这是一个store page fault</li>
<li>pid</li>
<li>SEPC寄存器值</li>
<li>出错的虚拟内存地址，STVAL寄存器：0x4008</li>
</ul>
<p>以上是错误信息。我们接下来看看如何能够聪明的处理这里的page fault。</p>
<p>usertrap:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// handle an interrupt, exception, or system call from user space.</span></span><br><span class="line"><span class="comment">// called from trampoline.S</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">usertrap</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	<span class="keyword">int</span> which_dev = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> ((r_sstatus() &amp; SSTATUS_SPP) != <span class="number">0</span>)</span><br><span class="line">		panic(<span class="string">&quot;usertrap: not from user mode&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="comment">// send interrupts and exceptions to kerneltrap(),</span></span><br><span class="line">	<span class="comment">// since we&#x27;re now in the kernel.</span></span><br><span class="line">	w_stvec((uint64)kernelvec);</span><br><span class="line"></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">proc</span> *<span class="title">p</span> =</span> myproc();</span><br><span class="line"></span><br><span class="line">	<span class="comment">// save user program counter.</span></span><br><span class="line">	p-&gt;trapframe-&gt;epc = r_sepc();</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (r_scause() == <span class="number">8</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="comment">// system call</span></span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (p-&gt;killed)</span><br><span class="line">			<span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line"></span><br><span class="line">		<span class="comment">// sepc points to the ecall instruction,</span></span><br><span class="line">		<span class="comment">// but we want to return to the next instruction.</span></span><br><span class="line">		p-&gt;trapframe-&gt;epc += <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// an interrupt will change sstatus &amp;c registers,</span></span><br><span class="line">		<span class="comment">// so don&#x27;t enable until done with those registers.</span></span><br><span class="line">		intr_on();</span><br><span class="line"></span><br><span class="line">		syscall();</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> ((which_dev = devintr()) != <span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="comment">// ok</span></span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span></span><br><span class="line">	&#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;usertrap(): unexpected scause %p pid=%d\n&quot;</span>, r_scause(), p-&gt;pid);</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;            sepc=%p stval=%p\n&quot;</span>, r_sepc(), r_stval());</span><br><span class="line">		p-&gt;killed = <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (p-&gt;killed)</span><br><span class="line">		<span class="built_in">exit</span>(<span class="number">-1</span>);</span><br><span class="line"></span><br><span class="line">	<span class="comment">// give up the CPU if this is a timer interrupt.</span></span><br><span class="line">	<span class="keyword">if</span> (which_dev == <span class="number">2</span>)</span><br><span class="line">		yield();</span><br><span class="line"></span><br><span class="line">	usertrapret();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>usertrap</code>根据不同的<code>SCAUSE</code>完成不同操作。</p>
<p>Chapter6中是SCAUSE == 8时进入的trap，如果SCAUSE不等于8，接下来会检查是否有任何的设备中断，如果有的话处理相关的设备中断。如果两个条件都不满足，这里会打印一些信息，并且杀掉进程。</p>
<p>增加下scause为15时的内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> ((which_dev = devintr()) != <span class="number">0</span>)</span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// ok</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">else</span> <span class="keyword">if</span> (r_scause() == <span class="number">15</span>)</span><br><span class="line">&#123;</span><br><span class="line">	uint64 va = r_stval();</span><br><span class="line">	<span class="built_in">printf</span>(<span class="string">&quot;page fault %p\n&quot;</span>, va);</span><br><span class="line">	uint64 ka = (uint64)kalloc();</span><br><span class="line">	<span class="keyword">if</span> (ka == <span class="number">0</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		p-&gt;killed = <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span></span><br><span class="line">	&#123;</span><br><span class="line">		<span class="built_in">memset</span>((<span class="keyword">void</span> *)ka, <span class="number">0</span>, PGSIZE);</span><br><span class="line">		va = PGROUNDDOWN(va);</span><br><span class="line">		<span class="keyword">if</span> (mappages(p-&gt;pagetable, va, PGSIZE, ka, PTE_W | PTE_U | PTE_R) != <span class="number">0</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			kfree((<span class="keyword">void</span> *)ka);</span><br><span class="line">			p-&gt;killed = <span class="number">1</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>新分配的内存为0表示没物理内存分配了，杀掉进程；有的话，将物理内存page指向用户地址空间中合适的虚拟内存地址。</p>
<p>具体来说，我们首先将虚拟地址向下取整，这里引起page fault的虚拟地址是0x4008，向下取整之后是0x4000。之后我们将物理内存地址跟取整之后的虚拟内存地址的关系加到page table中。对应的PTE需要设置常用的权限标志位。</p>
<figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> hi</span><br><span class="line">page fault 0x0000000000004008</span><br><span class="line">page fault 0x0000000000013f48</span><br><span class="line">panic: uvmunmap: not mapped</span><br><span class="line">QEMU: Terminated</span><br></pre></td></tr></table></figure>

<p>但是并没有正常工作。uvmunmap在报错，它尝试unmap的page并不存在。这里unmap的内存是之前lazy allocation但是又没有实际分配的内存。</p>
<p>所以对于这个内存，并没有对应的物理内存。所以在uvmunmap函数中，当PTE的v标志位为0并且没有对应的mapping，这并不是一个实际的panic，这是预期的行为，改为continue：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">uvmunmap</span><span class="params">(<span class="keyword">pagetable_t</span> pagetable, uint64 va, uint64 npages, <span class="keyword">int</span> do_free)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">	uint64 a;</span><br><span class="line">	<span class="keyword">pte_t</span> *pte;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> ((va % PGSIZE) != <span class="number">0</span>)</span><br><span class="line">		panic(<span class="string">&quot;uvmunmap: not aligned&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (a = va; a &lt; va + npages * PGSIZE; a += PGSIZE)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="keyword">if</span> ((pte = walk(pagetable, a, <span class="number">0</span>)) == <span class="number">0</span>)</span><br><span class="line">			panic(<span class="string">&quot;uvmunmap: walk&quot;</span>);</span><br><span class="line">		<span class="keyword">if</span> ((*pte &amp; PTE_V) == <span class="number">0</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">continue</span>;</span><br><span class="line">			<span class="comment">// panic(&quot;uvmunmap: not mapped&quot;);</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (PTE_FLAGS(*pte) == PTE_V)</span><br><span class="line">			panic(<span class="string">&quot;uvmunmap: not a leaf&quot;</span>);</span><br><span class="line">		<span class="keyword">if</span> (do_free)</span><br><span class="line">		&#123;</span><br><span class="line">			uint64 pa = PTE2PA(*pte);</span><br><span class="line">			kfree((<span class="keyword">void</span> *)pa);</span><br><span class="line">		&#125;</span><br><span class="line">		*pte = <span class="number">0</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>之后就可以直接执行了。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ echo hi</span><br><span class="line">page fault <span class="number">0x0000000000004008</span></span><br><span class="line">page fault <span class="number">0x0000000000013f48</span></span><br><span class="line">hi</span><br></pre></td></tr></table></figure>

<blockquote>
<p>  学生提问：我并不能理解为什么在uvmunmap中可以直接改成continue？</p>
<p>  Frans教授：之前的panic表明，我们尝试在释放一个并没有map的page。怎么会发生这种情况呢？唯一的原因是sbrk增加了p-&gt;sz，但是应用程序还没有使用那部分内存。因为对应的物理内存还没有分配，所以这部分新增加的内存的确没有映射关系。我们现在是lazy allocation，我们只会为需要的内存分配物理内存page。如果我们不需要这部分内存，那么就不会存在map关系，这非常的合理。相应的，我们对于这部分内存也不能释放，因为没有实际的物理内存可以释放，所以这里最好的处理方式就是continue，跳过并处理下一个page。</p>
<p>  学生提问：在uvmunmap中，我认为之前的panic存在是有理由的，我们是不是应该判断一下，然后对于特定的场景还是panic？</p>
<p>  Frans教授：为什么之前的panic会存在？对于未修改的XV6，永远也不会出现用户内存未map的情况，所以一旦出现这种情况需要panic。但是现在我们更改了XV6，所以我们需要去掉这里的panic，因为之前的不可能变成了可能。</p>
</blockquote>
<h2 id="Zero-Fill-On-Demand"><a href="#Zero-Fill-On-Demand" class="headerlink" title="Zero Fill On Demand"></a>Zero Fill On Demand</h2><p>这节是基于page fault 和page table可以做的其他酷的事情。</p>
<p>BSS区域包含了未被初始化或者初始化为0的全局或者静态变量。</p>
<p>BSS段或许有很多个page，但是所有page都为0，调优的方法：在物理内存中，我只需要分配一个page，这个page的内容全是0，然后将所有虚拟地址空间的全0的page都map到这一个物理page上。这样至少在程序启动的时候能节省大量的物理内存分配。</p>
<p><img src="/2022/07/07/6.s081/image-20220720211117628.png" alt="image-20220720211117628"></p>
<p>这个PTE是只读的。如果更改BSS的一两个变量的值时，会page fault，解决办法：物理内存申请一个新的page，memset(0)，之后更新这个page的映射关系，这个PTE设置为可读可写。</p>
<h2 id="Copy-On-Write-Fork"><a href="#Copy-On-Write-Fork" class="headerlink" title="Copy On Write Fork"></a>Copy On Write Fork</h2><p>也是一个常见的优化，有时叫COW fork。</p>
<p>shell处理指令时，会fork子进程，之后exec，现在的情况是，fork创建了Shell地址空间的一个完整的拷贝，而exec做的第一件事情就是丢弃这个地址空间，取而代之的是一个包含了echo的地址空间。这里看起来有点浪费。</p>
<p>所以对于这个特定场景有一个非常有效的优化：当我们创建子进程时，直接共享父进程的物理内存page。</p>
<p>这里要小心些，因为隔离性，有些更新对父进程应不可见。</p>
<p>首先先把父子进程的PTE都设置为只读，之后修改的时候会page fault，分配新的物理page，然后将page fault相关的物理内存page拷贝到新分配的物理内存page中，并将新分配的物理内存page映射到子进程。新分配的物理内存page只对子进程的地址空间可见，设置PTE为可读写，原来page fault的物理page，只对父进程可见，相应的PTE对父进程也为可读写了。</p>
<p>PTE中有个copy-on-write标志位，RSW。</p>
<h2 id="Demand-Paging"><a href="#Demand-Paging" class="headerlink" title="Demand Paging"></a>Demand Paging</h2><p>也是一个流行的功能。</p>
<p>exec时os会加载text，data区域，并且以eager的方式将这些区域加载进page table。这里可以在稍后加载进page table。</p>
<p>so，对于exec，虚拟地址中，为text和data分配好地址段，但是相应的PTE并不对应任何物理内存page。对于这些PTE，只需要将valid bit位设置为0即可。</p>
<p>应用程序是从地址0开始运行。text区域从地址0开始向上增长。位于地址0的指令是会触发第一个page fault的指令。如何处理？这些page是on-demand page。需要在某个地方记录了这些page对应的程序文件，在page fault handler中需要从程序文件中读取page数据，加载到内存中；之后将内存page映射到page table；最后再重新执行指令，就可以执行了。</p>
<p>dirty page是曾经被写过的page，而non-dirty page是只被读过，但是没有被写过的page。</p>
<h2 id="Memory-Mapped-Files"><a href="#Memory-Mapped-Files" class="headerlink" title="Memory Mapped Files"></a>Memory Mapped Files</h2><p>将完整或者部分文件加载到内存中，这样就可以通过内存地址相关的load或者store指令来操纵文件。为了支持这个功能，一个现代的操作系统会提供一个叫做mmap的系统调用。这个系统调用会接收一个虚拟内存地址（VA），长度（len），protection，一些标志位，一个打开文件的文件描述符，和偏移量（offset）。</p>
<h1 id="Chapter9"><a href="#Chapter9" class="headerlink" title="Chapter9"></a>Chapter9</h1><p>产生中断后，操作系统需要做的是，保存当前的工作，处理中断，处理完成之后再恢复之前的工作。</p>
<p>中断与系统调用主要有3个小的差别：</p>
<ol>
<li>异步（asynchronous）。当硬件生成中断时，Interrupt handler与当前运行的进程在CPU上没有任何关联。但如果是系统调用的话，系统调用发生在运行进程的context下。</li>
<li>并发（concurrency）。对于中断来说，CPU和生成中断的设备是并行的在运行。网卡自己独立的处理来自网络的packet，然后在某个时间点产生中断，但是同时，CPU也在运行。所以我们在CPU和设备之间是真正的并行的，我们必须管理这里的并行。</li>
<li>program device。我们这节主要关注外部设备，例如网卡，UART，而这些设备需要被编程。每个设备都有一个编程手册，就像RISC-V有一个包含了指令和寄存器的手册一样。设备的编程手册包含了它有什么样的寄存器，它能执行什么样的操作，在读写控制寄存器的时候，设备会如何响应。不过通常来说，设备的手册不如RISC-V的手册清晰，这会使得对于设备的编程会更加复杂。</li>
</ol>
<p>接下来讨论终端中的$与按下键盘后ls是怎样在console显示出来的。</p>
<p>首先是中断的硬件部分。拿UART0举例，UART0会映射到内核内存地址的某处，而所有的物理内存都映射在地址空间的0x80000000之上。</p>
<p>下面是中断的软件部分。管理设备的代码称为驱动，所有的驱动都在内核中。</p>
<p>大部分的驱动都有bottom&#x2F;top两个部分：</p>
<ul>
<li>bottom部分通常是Interrupt handler。当一个中断送到了CPU，并且CPU设置接收这个中断，CPU会调用相应的Interrupt handler。Interrupt handler并不运行在任何特定进程的context中，它只是处理中断。</li>
<li>top部分是用户部分或者内核调用的接口</li>
</ul>
<p>通常情况下，驱动中会有一些队列（或者说buffer），top部分的代码会从队列中读写数据，而Interrupt handler（bottom部分）同时也会向队列中读写数据。</p>
<p>也没啥想看的，都在跟源码，，passpass</p>
<h1 id="Chapter10"><a href="#Chapter10" class="headerlink" title="Chapter10"></a>Chapter10</h1><p>本章内容为锁相关。</p>
<blockquote>
<p>  首先，我们来看一下为什么我们需要锁？故事要从应用程序想要使用多个CPU核开始。使用多个CPU核可以带来性能的提升，如果一个应用程序运行在多个CPU核上，并且执行了系统调用，那么内核需要能够处理并行的系统调用。如果系统调用并行的运行在多个CPU核上，那么它们可能会并行的访问内核中共享的数据结构。到目前为止，你们也看到了XV6有很多共享的数据结构，例如proc、ticks和我们之后会看到的buffer cache等等。当并行的访问数据结构时，例如一个核在读取数据，另一个核在写入数据，我们需要使用锁来协调对于共享数据的更新，以确保数据的一致性。所以，我们需要锁来控制并确保共享的数据是正确的。</p>
<p>  但是实际的情况有些令人失望，因为我们想要通过并行来获得高性能，我们想要并行的在不同的CPU核上执行系统调用，但是如果这些系统调用使用了共享的数据，我们又需要使用锁，而锁又会使得这些系统调用串行执行，所以最后锁反过来又限制了性能。</p>
</blockquote>
<p>使用锁为了保证正确性，一个共享数据同时被读写时，如果没有锁，可能会出现race condition，条件竞争，进而导致程序出错。</p>
<h1 id="Chapter11"><a href="#Chapter11" class="headerlink" title="Chapter11"></a>Chapter11</h1><p>跳跳跳，编译原理</p>
<blockquote>
<p>  一些6.s081的博客</p>
<ol>
<li><a target="_blank" rel="noopener" href="http://doraemonzzz.com/tags/6-S081/">http://doraemonzzz.com/tags/6-S081/</a></li>
<li><a target="_blank" rel="noopener" href="https://fanxiao.tech/posts/MIT-6S081-notes/">https://fanxiao.tech/posts/MIT-6S081-notes/</a></li>
<li><a target="_blank" rel="noopener" href="https://www.cnblogs.com/weijunji/tag/XV6/">https://www.cnblogs.com/weijunji/tag/XV6/</a></li>
<li><a target="_blank" rel="noopener" href="http://xv6.dgs.zone/labs/use_git/git1.html">http://xv6.dgs.zone/labs/use_git/git1.html</a></li>
</ol>
</blockquote>

                                                                    </div>
                                                                    
                                                                        <div class="prev-or-next">
                                                                            <div class="post-foot-next">
                                                                                
                                                                                    <a href="/2022/07/02/2022-6-29-BypassAVDynamics/" target="_self">
                                                                                        <i class="iconfont icon-chevronleft"></i>
                                                                                        <span>Prev</span>
                                                                                    </a>
                                                                                    
                                                                            </div>
                                                                            <div class="post-attach">
                                                                                <!-- <span class="post-pubtime">
              <i class="iconfont icon-updatetime" title="Update time"></i>
              2022-07-07
            </span> -->

                                                                                
                                                                                            <span class="post-categories">
          <!-- <i class="iconfont icon-bookmark" title="Categories"></i> -->
          
          <!-- <span class="span--category">
            <a href="/categories/Technology/" title="Technology">
              <b>#</b> Technology
            </a>
          </span> -->
                                                                                            
                                                                                                </span>
                                                                                                
                                                                                    <span class="post-tags">
          <!-- <i class="iconfont icon-tags" title="Tags"></i> -->
          
          <!-- <span class="span--tag">
            <a href="/tags/OS/" title="OS">
              <b>#</b> OS
            </a>
          </span> -->
                                                                                    
                                                                                        </span>
                                                                                        
                                                                            </div>
                                                                            <div class="post-foot-prev">
                                                                                
                                                                                    <a href="/2022/07/10/2022-7-10-oslab/" target="_self">
                                                                                        <span>Next</span>
                                                                                        <i class="iconfont icon-chevronright"></i>
                                                                                    </a>
                                                                                    
                                                                            </div>
                                                                        </div>
                                                                        
                                                                </div>
                                                                
  <div id="btn-catalog" class="btn-catalog">
    <i class="iconfont icon-catalog"></i>
  </div>
  <div class="post-catalog hidden" id="catalog">
    <div class="title">Contents</div>
    <div class="catalog-content">
      
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter1"><span class="toc-text">Chapter1</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#6-s081"><span class="toc-text">6.s081</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E7%BB%93%E6%9E%84"><span class="toc-text">操作系统结构</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#read-write-exit%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8"><span class="toc-text">read,write,exit系统调用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%EF%BC%9A"><span class="toc-text">环境搭建：</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#fork"><span class="toc-text">fork</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#close"><span class="toc-text">close</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#dup"><span class="toc-text">dup</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pipe"><span class="toc-text">pipe</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#exec%EF%BC%8Cwait%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8"><span class="toc-text">exec，wait系统调用</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter3"><span class="toc-text">Chapter3</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%96%E8%AF%91%E8%BF%90%E8%A1%8Ckernel"><span class="toc-text">编译运行kernel</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#xv6%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B"><span class="toc-text">xv6启动过程</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter4"><span class="toc-text">Chapter4</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#kvminit%E5%87%BD%E6%95%B0"><span class="toc-text">kvminit函数</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter5"><span class="toc-text">Chapter5</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter6"><span class="toc-text">Chapter6</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#trap%E7%9A%84%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B"><span class="toc-text">trap的执行流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ECALL%E6%8C%87%E4%BB%A4%E4%B9%8B%E5%89%8D%E7%8A%B6%E6%80%81"><span class="toc-text">ECALL指令之前状态</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ECALL%E6%8C%87%E4%BB%A4%E4%B9%8B%E5%90%8E%E7%8A%B6%E6%80%81"><span class="toc-text">ECALL指令之后状态</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter8"><span class="toc-text">Chapter8</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#page-fault-basics"><span class="toc-text">page fault basics</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#lazy-page-allocation"><span class="toc-text">lazy page allocation</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Zero-Fill-On-Demand"><span class="toc-text">Zero Fill On Demand</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Copy-On-Write-Fork"><span class="toc-text">Copy On Write Fork</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Demand-Paging"><span class="toc-text">Demand Paging</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Memory-Mapped-Files"><span class="toc-text">Memory Mapped Files</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter9"><span class="toc-text">Chapter9</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter10"><span class="toc-text">Chapter10</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Chapter11"><span class="toc-text">Chapter11</span></a></li></ol>
      
    </div>
  </div>

  
<script src="/js/catalog.js"></script>




                                                                    
                                                                        <div class="comments-container">
                                                                            







                                                                        </div>
                                                                        
                                                            </div>
                                                            
        
<div class="footer">
  <div class="social">
    <ul>
      
        <li>
          <a title="github" target="_blank" rel="noopener" href="https://github.com/Ghostasky">
            <i class="iconfont icon-github"></i>
          </a>
        </li>
      
        <li>
          <a title="twitter" target="_blank" rel="noopener" href="https://twitter.com/ghostasky">
            <i class="iconfont icon-twitter"></i>
          </a>
        </li>
      
    </ul>
  </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/Ghostasky">怕什么真理无穷，进一寸有进一寸的欢喜。</a>
        
    </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/zchengsite/hexo-theme-oranges">Copyright © 2022 Oranges</a>
        
    </div>
  
    
    <div class="footer-more">
      
        <a target="_blank" rel="noopener" href="https://github.com/zchengsite/hexo-theme-oranges">Theme by Oranges | Powered by Hexo</a>
        
    </div>
  
</div>

      </div>

      <div class="tools-bar">
        <div class="back-to-top tools-bar-item hidden">
  <a href="javascript: void(0)">
    <i class="iconfont icon-chevronup"></i>
  </a>
</div>


<script src="/js/backtotop.js"></script>



        
  <div class="search-icon tools-bar-item" id="search-icon">
    <a href="javascript: void(0)">
      <i class="iconfont icon-search"></i>
    </a>
  </div>

  <div class="search-overlay hidden">
    <div class="search-content" tabindex="0">
      <div class="search-title">
        <span class="search-icon-input">
          <a href="javascript: void(0)">
            <i class="iconfont icon-search"></i>
          </a>
        </span>
        
          <input type="text" class="search-input" id="search-input" placeholder="Search...">
        
        <span class="search-close-icon" id="search-close-icon">
          <a href="javascript: void(0)">
            <i class="iconfont icon-close"></i>
          </a>
        </span>
      </div>
      <div class="search-result" id="search-result"></div>
    </div>
  </div>

  <script type="text/javascript">
    var inputArea = document.querySelector("#search-input")
    var searchOverlayArea = document.querySelector(".search-overlay")

    inputArea.onclick = function() {
      getSearchFile()
      this.onclick = null
    }

    inputArea.onkeydown = function() {
      if(event.keyCode == 13)
        return false
    }

    function openOrHideSearchContent() {
      let isHidden = searchOverlayArea.classList.contains('hidden')
      if (isHidden) {
        searchOverlayArea.classList.remove('hidden')
        document.body.classList.add('hidden')
        // inputArea.focus()
      } else {
        searchOverlayArea.classList.add('hidden')
        document.body.classList.remove('hidden')
      }
    }

    function blurSearchContent(e) {
      if (e.target === searchOverlayArea) {
        openOrHideSearchContent()
      }
    }

    document.querySelector("#search-icon").addEventListener("click", openOrHideSearchContent, false)
    document.querySelector("#search-close-icon").addEventListener("click", openOrHideSearchContent, false)
    searchOverlayArea.addEventListener("click", blurSearchContent, false)

    var searchFunc = function (path, search_id, content_id) {
      'use strict';
      var $input = document.getElementById(search_id);
      var $resultContent = document.getElementById(content_id);
      $resultContent.innerHTML = "<ul><span class='local-search-empty'>First search, index file loading, please wait...<span></ul>";
      $.ajax({
        // 0x01. load xml file
        url: path,
        dataType: "xml",
        success: function (xmlResponse) {
          // 0x02. parse xml file
          var datas = $("entry", xmlResponse).map(function () {
            return {
              title: $("title", this).text(),
              content: $("content", this).text(),
              url: $("url", this).text()
            };
          }).get();
          $resultContent.innerHTML = "";

          $input.addEventListener('input', function () {
            // 0x03. parse query to keywords list
            var str = '<ul class=\"search-result-list\">';
            var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
            $resultContent.innerHTML = "";
            if (this.value.trim().length <= 0) {
              return;
            }
            // 0x04. perform local searching
            datas.forEach(function (data) {
              var isMatch = true;
              var content_index = [];
              if (!data.title || data.title.trim() === '') {
                data.title = "Untitled";
              }
              var orig_data_title = data.title.trim();
              var data_title = orig_data_title.toLowerCase();
              var orig_data_content = data.content.trim().replace(/<[^>]+>/g, "");
              var data_content = orig_data_content.toLowerCase();
              var data_url = data.url;
              var index_title = -1;
              var index_content = -1;
              var first_occur = -1;
              // only match artiles with not empty contents
              if (data_content !== '') {
                keywords.forEach(function (keyword, i) {
                  index_title = data_title.indexOf(keyword);
                  index_content = data_content.indexOf(keyword);

                  if (index_title < 0 && index_content < 0) {
                    isMatch = false;
                  } else {
                    if (index_content < 0) {
                      index_content = 0;
                    }
                    if (i == 0) {
                      first_occur = index_content;
                    }
                    // content_index.push({index_content:index_content, keyword_len:keyword_len});
                  }
                });
              } else {
                isMatch = false;
              }
              // 0x05. show search results
              if (isMatch) {
                str += "<li><a href='" + data_url + "' class='search-result-title'>" + orig_data_title + "</a>";
                var content = orig_data_content;
                if (first_occur >= 0) {
                  // cut out 100 characters
                  var start = first_occur - 20;
                  var end = first_occur + 80;

                  if (start < 0) {
                    start = 0;
                  }

                  if (start == 0) {
                    end = 100;
                  }

                  if (end > content.length) {
                    end = content.length;
                  }

                  var match_content = content.substr(start, end);

                  // highlight all keywords
                  keywords.forEach(function (keyword) {
                    var regS = new RegExp(keyword, "gi");
                    match_content = match_content.replace(regS, "<span class=\"search-keyword\">" + keyword + "</span>");
                  });

                  str += "<p class=\"search-result-abstract\">" + match_content + "...</p>"
                }
                str += "</li>";
              }
            });
            str += "</ul>";
            if (str.indexOf('<li>') === -1) {
              return $resultContent.innerHTML = "<ul><span class='local-search-empty'>No result<span></ul>";
            }
            $resultContent.innerHTML = str;
          });
        },
        error: function(xhr, status, error) {
          $resultContent.innerHTML = ""
          if (xhr.status === 404) {
            $resultContent.innerHTML = "<ul><span class='local-search-empty'>The search.xml file was not found, please refer to：<a href='https://github.com/zchengsite/hexo-theme-oranges#configuration' target='_black'>configuration</a><span></ul>";
          } else {
            $resultContent.innerHTML = "<ul><span class='local-search-empty'>The request failed, Try to refresh the page or try again later.<span></ul>";
          }
        }
      });
      $(document).on('click', '#search-close-icon', function() {
        $('#search-input').val('');
        $('#search-result').html('');
      });
    }

    var getSearchFile = function() {
        var path = "/search.xml";
        searchFunc(path, 'search-input', 'search-result');
    }
  </script>




        
  <div class="tools-bar-item theme-icon" id="switch-color-scheme">
    <a href="javascript: void(0)">
      <i id="theme-icon" class="iconfont icon-moon"></i>
    </a>
  </div>

  
<script src="/js/colorscheme.js"></script>





        
  
    <div class="share-icon tools-bar-item">
      <a href="javascript: void(0)" id="share-icon">
        <i class="iconfont iconshare"></i>
      </a>
      <div class="share-content hidden">
        
          <a class="share-item" href="https://twitter.com/intent/tweet?text=' + MIT6.S081_Note + '&url=' + https%3A%2F%2Fghostasky.github.io%2F2022%2F07%2F07%2F6.s081%2F + '" target="_blank" title="Twitter">
            <i class="iconfont icon-twitter"></i>
          </a>
        
        
          <a class="share-item" href="https://www.facebook.com/sharer.php?u=https://ghostasky.github.io/2022/07/07/6.s081/" target="_blank" title="Facebook">
            <i class="iconfont icon-facebooksquare"></i>
          </a>
        
      </div>
    </div>
  
  
<script src="/js/shares.js"></script>



      </div>
    </div>
  </body>
</html>
